Skip to content

viewer: selection callout, interaction sounds and drone mode - #1227

Merged
wass08 merged 4 commits into
mainfrom
feat/viewer-callouts
Nov 6, 2026
Merged

wass08 merged 4 commits into
mainfrom
feat/viewer-callouts

Conversation

@wass08

@wass08 wass08 commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

A viewer pass: selection, actions and camera modes.

  • Selection callout. Selecting an element in the viewer now shows the marketing house's callout: a dot on the element, a hairline, its name and kind ("Single-hung window", "Hinged door"), one pill per action and a close button. It replaces the floating On/Open buttons that popped on every item of a selected room. It works on parametric and baked scenes, and in the editor's preview.
    • Actions: open and close doors, windows and shutters; lights; a recipe item's moving parts; catalog toggles and sliders.
    • Rooms: a selected room gets the callout too, with its name and area.
    • Clicks: a click in orbit only selects now; nothing opens on click.
  • Sounds. Operating something plays its sound through a new core interaction:sound event, which the editor's SFX bus plays.
    • Closing: a door, window or recipe door swings, then latches on the frame it lands. Built-in leaves report through door: / window:animation-completed. Baked clips use the remaining clip time. Recipes use a new procedural:motion-settled event, backed by the motion controller's settled flag.
    • Pointer: hover, select and deselect tick like the house (useViewerUiSounds).
    • Walkthrough: E and click play the same sounds.
    • Assets: door, garage and shutter recordings come from the house; the switch, UI ticks and swing are rendered by scripts/synth-viewer-cues.ts.
  • Drone mode sits beside the walkthrough: a Drone button in the viewer bar and key hints in the walkthrough HUD. There is one flight implementation (drone-flight.ts, DroneController in viewer), and the snapshot capture drone now runs on it too.

How to test

  1. Open a scene in the viewer (preview mode, or the hosted viewer) and select a floor, then a room. The room shows a callout with its name and area; no buttons pop on its items.
  2. Click a door or a window. Expected:
    • The callout shows its name, kind and an Open/Closed pill.
    • The click itself does not open it.
    • The pill opens it with a sound. Closing plays a swing, then the latch as the leaf lands.
  3. Select a window with shutters: Closed and Shutters pills sit on one row, with × after them. Select a recipe cabinet: closing a door swings and claps when it lands; a drawer claps when it lands.
  4. Press × or Esc: the selection clears, with the deselect tick. Hovering elements ticks faintly.
  5. Click the Drone button in the bar. Expected:
    • Key hints show at the bottom.
    • WASD, Space/E and Q fly; Shift boosts; Alt slows.
    • P frees the cursor; Esc returns to orbit.
  6. In the editor's snapshot capture, Drone still flies as before.

Automated, run locally:

  • Tests: bun test in packages/viewer (1333 pass), packages/editor src/components src/lib (2594 pass), packages/core src/procedural-items, packages/nodes src/procedural-item.
  • Type check: tsgo --noEmit for core, viewer, editor and nodes.
  • Lint and i18n: bun run check (warnings only, all pre-existing), bun run i18n:check, bun scripts/i18n-literals.ts check.

New strings have fr/es/ja/zh-CN drafts flagged for review.

Screenshots / screen recording

Not added yet.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

Tested in the hosted community viewer on parametric and baked scenes (headless Chrome), not through the standalone app's bun dev.

🤖 Generated with Claude Code


Note

Medium Risk
Substantial viewer/editor interaction, audio, and camera-path changes—selection vs activation and sound timing could regress, but scope stays within immersive modes rather than auth or persistence.

Overview
Selection and baked GLB. Orbit clicks now only change selection; doors, windows, shutters, mechanisms, recipe parts, and catalog items expose actions through a new SelectionCallout (and GlbSelectionCallout on baked scenes) with localized titles and pills, plus room callouts for name and area. The old floating ControlWidget overlays on GLB scenes are removed.

Sounds. Core adds interaction:sound and procedural:motion-settled; the editor SFX bus maps them to new door/window/garage/shutter/panel/switch and UI cue clips. ProceduralMotionController exposes settled (open/slide rest; spins ignored) and procedural render/GLB playback emit motion-settled when a command completes. openingSettled / proceduralSettled coordinate swing-then-latch audio; walkthrough E/click and first-person playTargetSound share the mapping. useViewerUiSounds adds hover/select/deselect ticks outside walkthrough until the page is user-activated.

Drone. Shared drone-flight powers DroneController in the viewer, the editor snapshot/first-person drone path, and DroneHud key hints; useViewer tracks walkthroughMovement (walk | drone) so orbit vs walk vs drone modes stay distinct. Viewer controls bar gains a Drone button (desktop); exports and tests cover flight math and callout builders.

i18n / baseline. New viewer/editor strings for callouts, drone hints, and controls; literal baseline drops obsolete control-widget entries.

Reviewed by Cursor Bugbot for commit ee16c5e. Bugbot is set up for automated code reviews on this repo. Configure here.

wass08 and others added 4 commits November 6, 2026 14:15
A selected element in the viewer gets the marketing house's callout: a dot,
a hairline, its name and kind ("Single-hung window"), one pill per action
(open or close, lights, shutters, moving parts, sliders) and a close button.
It replaces the floating On/Open buttons that popped on every item of a
selected room, on both the parametric and the baked paths. A click in orbit
now only selects; doors open from the pill.

Operating something plays its sound (door, window, garage, shutters, switch)
through a new `interaction:sound` event the editor's SFX bus plays; the
walkthrough's E and click play the same sounds. Door and shutter recordings
come from the /next house, the switch from scripts/synth-switch-cues.ts.

The viewer gains drone mode beside the walkthrough: one flight
implementation (`drone-flight.ts`, `DroneController`) that the snapshot
capture drone now runs on too, a Drone button in the viewer bar, and a hint
strip in the walkthrough HUD.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The callout keeps the house's dusk palette on every scene (the light-theme
dark ink went grey over night scenes), its actions stay on one row with the
close button after the last pill, and the pills take the house's sizes.

A selected room gets the callout too: its name and area at a point inside
it, and the close button.

Hovering, selecting and deselecting tick as on the house (its "Minimal" kit
cues, MIT); the switches use its pitch sweeps. scripts/synth-viewer-cues.ts
renders all five, replacing synth-switch-cues.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closing a door or a window now swings shut, then latches on the frame the
leaf lands: built-in leaves and sashes wait for their animation-completed
event (`openingSettled`), baked ones for the rest of their reversed clip.
The swing is a new soft air cue (door_swing.mp3, synthesized in
scripts/synth-viewer-cues.ts); windows play both quieter. Shutters clap as
they land; garage doors keep their motor from the press. The walkthrough's E
and click time their sounds the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A procedural item's hinged part (a cabinet door, a lid) swings shut and claps
as it lands; a sliding part (a drawer) claps as it lands; both open with the
panel recording, as the marketing house plays its cabinets. The motion
controller reports when its opening and sliding parts are at rest
(`settled`, ignoring spinning ones), both runtimes announce it as
`procedural:motion-settled` for the commanded run, and `proceduralSettled`
lets the callout and the walkthroughs wait for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ee16c5e. Configure here.


const target = interactableTargetRef.current ?? resolveInteractableTarget()
if (!target) return
playTargetSound(target)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Close latch can fire too early

Medium Severity

Walkthrough plays the closing sound by waiting for the next settle event before the toggle runs. A second E while a door, window, or recipe part is still opening therefore hears the in-flight open finish as a latch, and the real close never gets one. Pending waits are also not tied to the command that started them, so reversing an action can clap on the wrong landing.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ee16c5e. Configure here.

else openIds.current.delete(id)
setOpenVersion((version) => version + 1)
// Played backwards from where it stands, the clip lands after its current time.
if (!willOpen) return new Promise<void>((resolve) => setTimeout(resolve, action.time * 1000))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Baked close timer is never cancelled

Medium Severity

Closing a baked door or window returns a setTimeout for the remaining clip time and never clears it. Opening or closing again before that timer fires still lets the old callback run, so the latch can sound while the leaf is opening or twice on a later close.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ee16c5e. Configure here.

@wass08
wass08 merged commit cd77647 into main Nov 6, 2026
5 checks passed
@wass08
wass08 deleted the feat/viewer-callouts branch November 6, 2026 19:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant